package ro.flashbite.game_engine.controller
{
	import flash.display.Sprite;
	import flash.system.System;
	import flash.utils.Dictionary;
	
	import ro.flashbite.game_engine.consts.StatesGame;
	import ro.flashbite.game_engine.data.intefaces.IAppDataStorage;
	import ro.flashbite.game_engine.interfaces.IGameEngine;
	import ro.flashbite.game_engine.interfaces.ILevelToEngine;
	import ro.flashbite.game_engine.proxy.LoadingProxy;
	import ro.flashbite.game_engine.view.BaseLevel;
	import ro.flashbite.game_engine.view.LoadingScreen;
	import ro.flashbite.helpers.DictionaryHelpers;
	import ro.flashbite.interfaces.ISleepWakeupDestroy;
	import ro.flashbite.loaders.AssetManager;
	import ro.flashbite.sound.SoundManager;
	
	/**
	 * Class that make a load of one level assisted by a LoadingProxy.
	 * 
	 * v 1.0
	 * 
	 * @author Adrian Barbu 
	 */	
	public final class LevelManager implements ISleepWakeupDestroy, ILevelToEngine
	{
		private var _appContainer:Sprite;
		
		private var _currentState:uint;
		
		private var _soundManager:SoundManager;
		private var _assetManager:AssetManager;
		
		private var _assetsXML:XML;
		private var _loadingScreen:LoadingScreen;
		
		private var _onCompleteLoadFunction:Function;
		
		private var _loadingProxy:LoadingProxy;
		
		private var _currentLevelClass:Class;
		private var _currentLevel:BaseLevel;
		private var _currentLevelID:String;
		
		private var _levelDict:Dictionary;
		private var _levelList:Array;
		
		private var _gameEngine:IGameEngine;
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function LevelManager(appContainer:Sprite, 
									 assetsXML:XML, 
									 levelList:Array, 
									 loadingScreen:LoadingScreen, 
									 assetMng:AssetManager, 
									 sndManager:SoundManager,
									 gameEngine:IGameEngine)
		{
			_constructor(appContainer, assetsXML, levelList, loadingScreen, assetMng, sndManager, gameEngine);
		}
		private function _constructor(appContainer:Sprite, 
									  assetsXML:XML, 
									  levelList:Array, 
									  loadingScreen:LoadingScreen, 
									  assetMng:AssetManager, 
									  sndManager:SoundManager,
									  gameEngine:IGameEngine):void
		{
			_currentState = StatesGame.INACTIVE;
			
			_appContainer = appContainer;
			_assetsXML = assetsXML;
			_levelList = levelList;
			_levelDict = new Dictionary(true);
			for each (var lvlProp:Object in _levelList) {
				_levelDict[lvlProp.classID] = lvlProp.className;
			}
			_loadingScreen = loadingScreen;
			_assetManager = assetMng;
			_soundManager = sndManager;
			_gameEngine = gameEngine;
			
			_loadingProxy = new LoadingProxy(_loadingScreen);
		}
		
		// ================================================================================================================================
		// ISleepWakeupDestroy
		// ================================================================================================================================
		
		public function sleep():void
		{
			if (_currentState == StatesGame.PLAYING) {
				if (_currentLevel) {
					//sleep current level
					_currentLevel.sleep();
					_currentState = StatesGame.PAUSED;
				}
			} else if (_currentState == StatesGame.INITIALIZING) {
				//mark to show pause menu after initializing is complete....
				_currentState = StatesGame.PAUSED;
			}
		}
		
		public function wakeup():void
		{
			if (_currentState == StatesGame.PAUSED) {
				if (_currentLevel) {
					_currentLevel.wakeup();
					_currentState = StatesGame.PLAYING;
				}
			}
			
			if (_soundManager && _currentLevelID) {
				_soundManager.wakeup(_currentLevelID); //resume only level sounds
			}
		}
		
		public function destroy():void
		{
			_appContainer = null;
			_soundManager = null;
			_assetManager = null;
			_assetsXML = null;
			_loadingScreen = null;
			_onCompleteLoadFunction = null;
			
			if (_loadingProxy) {
				_loadingProxy.destroy();
				_loadingProxy = null;
			}
			_currentLevelClass = null;
			_currentLevelID = null;
			if (_currentLevel) {
				if (_currentLevel.parent) _currentLevel.parent.removeChild(_currentLevel);
				_currentLevel.destroy();
				_currentLevel = null;
			}
			
			_levelList = null;
			if (_levelDict) {
				DictionaryHelpers.clear(_levelDict);
				_levelDict = null;
			}
			_gameEngine = null;
		}
		
		// ================================================================================================================================
		// ILevelToEngine
		// ================================================================================================================================
		
		// global callbacks to gameEngine
		public function openPauseLevelMenu(id:String = null):void { _gameEngine.openPauseLevelMenu(id); }
		public function openCompleteLevelMenu(id:String = null, data:* = null):void { _gameEngine.openCompleteLevelMenu(id, data); }
		public function openCompleteGameMenu(id:String = null, data:* = null) :void { _gameEngine.openCompleteGameMenu(id, data);  }
		public function openGameOverLevelMenu(id:String = null, data:* = null):void { _gameEngine.openGameOverLevelMenu(id, data); }
		
		// callbacks in MenuManager
		public function setLevelInitComplete(id:String):void
		{
			if (_currentLevelID == id) {
				//hide loading screen
				if (_loadingScreen.parent) _loadingScreen.parent.removeChild(_loadingScreen);
				//add level to screen + start it
				_appContainer.addChild(_currentLevel);
				_currentLevel.start();
				//if it happens to be now in a application pause state, open pause menu
				if (_currentState == StatesGame.PAUSED) openPauseLevelMenu();
				else _currentState = StatesGame.PLAYING;
			} else {
				throw new Error("Given level with init complete : " + id + ", doesn't match the current level in LevelManager : " + _currentLevelID);
			}
		}
			
		// IGameEngine functions
			//asset manager getting data
		public function getLevelAssetContent(id:String, type:String):Object { return _gameEngine.getLevelAssetContent(id, type); }
		public function getGlobalAssetContent(id:String, type:String):Object { return _gameEngine.getGlobalAssetContent(id, type); }
			//sound play/stop management
		public function playSound(ids:Array, closure:Function = null, loops:int = 1):Boolean { return _gameEngine.playSound(ids, closure, loops); }
		public function playSoundNoOverwrite(ids:Array, closure:Function = null, loops:int = 1):Boolean { return _gameEngine.playSoundNoOverwrite(ids, closure, loops); }
		public function stopSound(ids:Array):void { _gameEngine.stopSound(ids); }
		public function stopAllSounds():void { _gameEngine.stopAllSounds(); }
		public function getSoundLength(soundID:String):Number { return _gameEngine.getSoundLength(soundID); }
			//global saving data
		public function saveSharedData(id:String, data:Object, overwrite:Boolean = true):void { _gameEngine.saveSharedData(id, data, overwrite); }
		public function getSharedData(id:String):Object { return _gameEngine.getSharedData(id); }
			//global application storage
		public function getAppDataStorage():IAppDataStorage { return _gameEngine.getAppDataStorage(); }
		public function saveAppDataStorage():void { _gameEngine.saveAppDataStorage(); }
			//console callbacks
		public function addConsoleBinding(bindingID:String, bindingFunction:Function, bindingParam:Array = null, overwriteSame:Boolean = true):void { _gameEngine.addConsoleBinding(bindingID, bindingFunction, bindingParam, overwriteSame); }
		public function removeConsoleBinding(bindingID:String):void { _gameEngine.removeConsoleBinding(bindingID); }
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		public function loadLevel(levelID:String, characterID:String):void
		{
			unloadLevel(levelID);
			//save current level
			_currentLevelID = levelID;
			_currentLevelClass = _levelDict[_currentLevelID];
			if (_currentLevelID == null || _currentLevelClass == null) {
				throw new Error("Unable to create level with id  : " + levelID);
			} else {
				if (characterID == null) throw new Error("Unable to get currentCharacterID");
				//add loading screen
				_appContainer.addChild(_loadingScreen);
				//reset last proxy
				if (_loadingProxy) {
					_loadingProxy.reset();
				}
				//append character assets in current level assets list
				var moduleXML:XML = _assetsXML.module.(@id == _currentLevelID)[0],
					characterAssetsXMLList:XMLList = XML(XML(_assetsXML.module.(@id == "characters")[0]).character.(@id == characterID)[0]).asset,
					finalModuleXML:XML = moduleXML.copy().appendChild(characterAssetsXMLList);
				
				//start the loading
				_loadingProxy.loadAndRegisterAssets(_assetManager, _soundManager, finalModuleXML, _currentLevelID, onCompleteLoading); 
			}
			
			_currentState = StatesGame.LOADING;
		}
		
		public function replayLevel():void
		{
			if (_currentLevel) {
				_currentLevel.reset();
				_currentLevel.start();
				_currentState = StatesGame.PLAYING;
			} else {
				_currentState = StatesGame.INACTIVE;
			}
		}
		
		public function unloadLevel(id:String):void
		{
			if (_currentLevel && _currentLevel.ID == id) {
				_currentLevel.destroy();
				if (_currentLevel.parent) _currentLevel.parent.removeChild(_currentLevel);
				_currentLevel = null;
			}
			
			if (_loadingProxy && _loadingProxy.moduleID == id) {
				_loadingProxy.reset(); //unregister sounds from SoundsManager; unload current module from AssetManager
			}
			
			_currentState = StatesGame.INACTIVE;
			
			//force unload after destroying level and unloading assets from assetManager
			System.gc();
		}
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		private function onCompleteLoading():void
		{
			_currentState = StatesGame.INITIALIZING;
			_currentLevel = new _currentLevelClass(_currentLevelID, this);
			_currentLevel.initialize();
		}
		
		// ================================================================================================================================
		// GETTERS, SETTERS
		// ================================================================================================================================
		
		public function get currentState()		: uint 	{ return _currentState; }
		public function set currentState(v:uint): void 	{ _currentState = v; 	}

		public function get currentLevelID()	: String 	{ return _currentLevelID; 	}
	}
}